package cn.wolfcode.service.impl;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.domain.AccountLog;
import cn.wolfcode.domain.OperateIntergralVo;
import cn.wolfcode.mapper.AccountLogMapper;
import cn.wolfcode.mapper.AccountTransactionMapper;
import cn.wolfcode.mapper.UsableIntegralMapper;
import cn.wolfcode.service.IUsableIntegralService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.msg.IntergralCodeMsg;
import com.alibaba.fastjson.JSON;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.BusinessActionContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;


@Slf4j
@Service
public class UsableIntegralServiceImpl implements IUsableIntegralService {
    @Autowired
    private UsableIntegralMapper usableIntegralMapper;
    @Autowired
    private AccountTransactionMapper accountTransactionMapper;
    @Autowired
    private AccountLogMapper accountLogMapper;

    @Override
    public String tryIntegralPay(@BusinessActionContextParameter(paramName = "vo") OperateIntergralVo vo, BusinessActionContext ctx) {
        // BusinessActionContextUtil.addContext("vo", vo);
        log.info("[积分支付-TRY] 执行积分支付预留方法：vo={}, ctx={}", JSON.toJSONString(vo), ctx);
        // 1. 先尝试扣除用户积分
        int row = usableIntegralMapper.freezeIntergral(vo.getUserId(), vo.getValue());
        // 2. 判断扣除是否成功
        if (row <= 0) {
            throw new BusinessException(IntergralCodeMsg.INTERGRAL_NOT_ENOUGH);
        }
        // 3. 生成交易流水号
        String tradeNo = genTradeNo();
        BusinessActionContextUtil.addContext("tradeNo", tradeNo);
        return tradeNo;
    }

    @Override
    public void confirmIntegralPay(BusinessActionContext ctx) {
        log.info("[积分支付-CONFIRM] 提交积分支付：ctx={}", ctx.getActionContext());
        String tradeNo = ctx.getActionContext("tradeNo", String.class);
        OperateIntergralVo vo = ctx.getActionContext("vo", OperateIntergralVo.class);

        // 扣除金额
        usableIntegralMapper.commitChange(vo.getUserId(), vo.getValue());
        // 保存流水记录
        buildAccountLog(tradeNo, vo, AccountLog.TYPE_DECR);
    }

    @Override
    public void cancelIntegralPay(BusinessActionContext ctx) {
        log.info("[积分支付-CANCEL] 回滚积分支付：ctx={}", ctx.getActionContext());
        OperateIntergralVo vo = ctx.getActionContext("vo", OperateIntergralVo.class);
        // 扣除冻结金额
        usableIntegralMapper.unFreezeIntergral(vo.getUserId(), vo.getValue());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String integralPay(OperateIntergralVo vo) {
        // 1. 先尝试扣除用户积分
        int row = usableIntegralMapper.decrIntegral(vo.getUserId(), vo.getValue());
        // 2. 判断扣除是否成功
        if (row <= 0) {
            throw new BusinessException(IntergralCodeMsg.INTERGRAL_NOT_ENOUGH);
        }
        // 3. 扣除成功, 生成账户变动流水记录
        AccountLog log = buildAccountLog(genTradeNo(), vo, AccountLog.TYPE_DECR);
        return log.getTradeNo();
    }

    private AccountLog buildAccountLog(String tradeNo, OperateIntergralVo vo, Integer type) {
        AccountLog log = new AccountLog();
        log.setAmount(vo.getValue());
        log.setInfo(vo.getInfo());
        log.setTradeNo(tradeNo);
        log.setGmtTime(new Date());
        log.setType(type);
        log.setOutTradeNo(vo.getPk());
        accountLogMapper.insert(log);
        return log;
    }

    private String genTradeNo() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + IdGenerateUtil.get().nextId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doRefund(OperateIntergralVo vo) {
        // 1. 基于订单编号查询支付记录
        AccountLog log = accountLogMapper.selectByOutTradeNoAndType(vo.getPk(), AccountLog.TYPE_DECR);
        // 2. 如果支付记录不存在, 说明流程有误
        if (log == null) {
            throw new BusinessException(IntergralCodeMsg.OP_INTERGRAL_ERROR);
        }
        // 3. 增加一条账户流水记录
        buildAccountLog(genTradeNo(), vo, AccountLog.TYPE_INCR);
        // 4. 获取上次支付的积分, 添加到用户账户积分
        usableIntegralMapper.addIntergral(vo.getUserId(), vo.getValue());
    }
}
